package com.license.logic.jess;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jess.Fact;
import jess.JessException;
import jess.RU;
import jess.Rete;
import jess.Value;
import jess.ValueVector;

import com.license.common.util.Constants;
import com.license.logic.alimentation.Food;
import com.license.logic.preference.Preferences;

public class JessRecomandation {

	private Rete engine = null;
	public JessRecomandation() {
		InputStream inStream = null;
		OutputStream outStream = null;
		InputStream cInStream = null;
		OutputStream dOutStream = null;
	 
	    	try{
	 
	    	    File afile =new File(Constants.REAL_LOCATION_RULES_FILE);
	    	    File bfile =new File(Constants.RULES_FILE);
	    		 
	    	    File cfile =new File(Constants.REAL_LOCATION_FACTS_FILE);
	    	    File dfile =new File(Constants.FACTS_FILE);
	 
	    	    inStream = new FileInputStream(afile);
	    	    outStream = new FileOutputStream(bfile);
	    	    
	    	    cInStream = new FileInputStream(cfile);
	    	    dOutStream = new FileOutputStream(dfile);

	 
	    	    byte[] buffer = new byte[1024];
	 
	    	    int length;
	    	    //copy the file content in bytes 
	    	    while ((length = inStream.read(buffer)) > 0){
	 
	    	    	outStream.write(buffer, 0, length);
	 
	    	    }
	    	    
	    	    while ((length = cInStream.read(buffer)) > 0){
	    	   	 
	    	    	dOutStream.write(buffer, 0, length);
	 
	    	    }
	 
	    	    inStream.close();
	    	    outStream.close();
	    	    
	    	    cInStream.close();
	    	    dOutStream.close();
	 
	    	    System.out.println("Facts File is copied successful!");
	 
	    	}catch(IOException e){
	    		e.printStackTrace();
	    	}
	    	try {
	    	// load fact and rules file 
			// create a reasoner
			 engine = new Rete();

			// load rules file
			engine.executeCommand("(batch " + Constants.RULES_FILE + ")");
			// load facts file
			
				engine.executeCommand("(load-facts " + Constants.FACTS_FILE + ")");
			} catch (JessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	
	public boolean checkIfFoodItemIsDiabetic(Food foodItem,
			List<Food> foodMeniu) throws JessException {

		String result = "";
		if(foodMeniu != null) {
			for(Food food : foodMeniu) {
				if(food.getName().equalsIgnoreCase(foodItem.getName())) 
					return false;
			}
		}
		try {
			
			Fact foodItemFact = new Fact("food_item_to_check", engine);
			foodItemFact.setSlotValue("__data",new Value(new ValueVector().add(new Value(foodItem.getName().replaceAll(" ", "_"), RU.ATOM)),RU.LIST));
			engine.assertFact(foodItemFact);
			Fact validFoodFact = new Fact("deasese", engine);
			validFoodFact.setSlotValue("__data",new Value(new ValueVector().add(new Value("diabetes", RU.ATOM)),RU.LIST));
			engine.assertFact(validFoodFact);
			// reason
			engine.run();
			// extract facts from Jess and list them in Java
			Value defauldFoodItem = engine.fetch("M");
			result = defauldFoodItem.stringValue(engine.getGlobalContext());
			engine.retract(foodItemFact);

		} catch (Exception je) {
			System.out.println("Message: " + je.getMessage());
			je.printStackTrace();
		}
		boolean diabeticCheckResult = result.equalsIgnoreCase("true") ? true : false;
		if(diabeticCheckResult) {
			diabeticCheckResult = validFood(foodMeniu, foodItem);
		} else {
			diabeticCheckResult = false;
		}
		return diabeticCheckResult;		
	}

	public String recomand(String deasese, Food foodToCheck,
			List<Food> preferences, List<Food> foodMeniu)
			throws JessException {

		String result = "";
		try {
			Fact diabetes = new Fact("deasese", engine);
			diabetes.setSlotValue("__data",new Value(new ValueVector()
							.add(new Value(deasese, RU.ATOM)), RU.LIST));
			engine.assertFact(diabetes);
			assertCustomFacts(engine, preferences, foodMeniu,false,foodToCheck);

			engine.run();
			// extract facts from Jess and list them in Java

			Value foodItem = engine.fetch("REC_FOOD");
			if (foodItem == null ) {
				Fact defaultFact = new Fact("default", engine);
				defaultFact.setSlotValue("__data",new Value(new ValueVector().add(new Value("another",RU.ATOM)), RU.LIST));

				engine.assertFact(defaultFact);
			//	engine.executeCommand("(facts)");
				engine.executeCommand("(run)");
			//	engine.executeCommand("(facts)");
			}

			Value defauldFoodItem = engine.fetch("REC_FOOD");
			result = defauldFoodItem.stringValue(engine.getGlobalContext()).replaceAll("_", " ");
			
			Fact defaultFact = new Fact("recomanded_before", engine);
			defaultFact.setSlotValue("__data",new Value(new ValueVector().add(new Value(result,RU.ATOM)), RU.LIST));

			engine.assertFact(defaultFact);
			engine.run();
		//	engine.executeCommand("(facts)");
			// ---------------- adaug food-ul recomandat pentru a verifica daca
			// in combinatie cu cele existente meniul ii ok--
		} catch (Exception je) {
			System.out.println("Message: " + je.getMessage());
			je.printStackTrace();
		}
		return result;
	}

	public boolean checkMeniu(List<Food> foodMeniu) throws JessException {

		String result = "";

		try {
			Fact isOk = new Fact("run_check_meniu", engine);
			isOk.setSlotValue("__data",	new Value(new ValueVector().add(new Value("true", RU.ATOM)),RU.LIST));
			engine.assertFact(isOk);
			
			List<Fact> menuFacts = assertCustomFacts(engine,null,foodMeniu,true,null);
			// extract facts from Jess and list them in Java
			engine.run();
			Value defauldFoodItem = engine.fetch("P");
			result = defauldFoodItem.stringValue(engine.getGlobalContext());
			
			for(Fact fact: menuFacts) {
				engine.retract(fact);
			}
			Fact stopExectionFact = new Fact("stop_execution", engine);
			stopExectionFact.setSlotValue("__data",new Value(new ValueVector().add(new Value("false", RU.ATOM)),RU.LIST));
			engine.assertFact(stopExectionFact);
			engine.run();

		} catch (Exception je) {
			System.out.println("Message: " + je.getMessage());
			je.printStackTrace();
		}
		return result.equalsIgnoreCase("true") ? true : false;
	}

	public boolean validFood(List<Food> foodMeniu, Food foodItem) throws JessException {

		String result = "";
		if (foodMeniu != null) {
			if(!checkMeniu(foodMeniu)) {
				foodMeniu.remove(foodMeniu.size() - 1);
				return false;
			}
			if(foodItem != null) {
				for (Food food : foodMeniu) {
					if (food.getName().equalsIgnoreCase(foodItem.getName()))
						return false;
				}
			}
		}
		try {
			List<Fact> menuFacts = assertCustomFacts(engine, null, foodMeniu,true,foodItem);
			Fact validFoodFact = new Fact("valid_if_food_item_is_part_of_menu", engine);
			validFoodFact.setSlotValue("__data",new Value(new ValueVector().add(new Value("true", RU.ATOM)),RU.LIST));
			engine.assertFact(validFoodFact);
			// reason
			engine.run();
			Value defauldFoodItem = engine.fetch("P");
			result = defauldFoodItem.stringValue(engine.getGlobalContext());
			for(Fact fact: menuFacts) {
				engine.retract(fact);
			}
			Fact stopExectionFact = new Fact("stop_execution", engine);
			stopExectionFact.setSlotValue("__data",new Value(new ValueVector().add(new Value("false", RU.ATOM)),RU.LIST));
			engine.assertFact(stopExectionFact);
			// reason
			engine.run();

		} catch (Exception je) {
			System.out.println("Message: " + je.getMessage());
			je.printStackTrace();
		}
		return result.equalsIgnoreCase("true") ? true : false;
	}
	
	private void buildFactsFile(String fileName) {
		try {
			String content = " ";

			File file = new File(fileName);
			// if file doesnt exists, then create it
			if (!file.exists()) {
				file.createNewFile();
			}
			FileWriter fw = new FileWriter(file.getAbsoluteFile());
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(content);
			bw.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private List<Fact> assertCustomFacts(Rete engine, List<Food> preferences,
			List<Food> foodMenu, boolean useForCheckMenu, Food foodItem)
			throws JessException {
		
		List<Fact> menuFacts = new ArrayList<Fact>();

		if (preferences != null) {

				for (Food foodItemm : preferences) {
					Fact meniu_food_item = new Fact("prefference_item", engine);
					meniu_food_item.setSlotValue("__data",new Value(new ValueVector().add(
												new Value(foodItemm.getName().replaceAll(" ", "_"), RU.ATOM)),RU.LIST));
					engine.assertFact(meniu_food_item);
					// reason
					engine.run();
				}
			}

		if (foodMenu != null) {
			int i = 0;
			for (Food food : foodMenu) {
				if (foodItem != null && food.getName().equalsIgnoreCase(foodItem.getName())) {
					break;
				} else {
					Fact meniu_food_item = null;
					if (useForCheckMenu) {
						meniu_food_item = new Fact("meniu_food_item" + i,engine);
					} else {
						meniu_food_item = new Fact("meniu_food_item", engine);
					}

					meniu_food_item.setSlotValue("__data",new Value(new ValueVector().add(
												new Value(food.getName().replaceAll(" ", "_"), RU.ATOM)),RU.LIST));
					menuFacts.add(meniu_food_item);
					i++;
				}}
				for (int j = i; j < 3; j++) {
					Fact meniu_food_item = null;
					meniu_food_item = new Fact("meniu_food_item" + j, engine);
					meniu_food_item.setSlotValue("__data",	new Value(
												new ValueVector().add(new Value("dummyFood", RU.ATOM)), RU.LIST));
					menuFacts.add(meniu_food_item);
				}
				if(foodItem != null) {
					Fact foodItemFact = new Fact("meniu_food_item3", engine);
					foodItemFact.setSlotValue("__data",new Value(new ValueVector().add(
											  new Value(foodItem.getName().replaceAll(" ", "_"), RU.ATOM)),RU.LIST));
					menuFacts.add(foodItemFact);
					}
			}
		for(Fact fact: menuFacts) {
			engine.assertFact(fact);
			engine.run();
		}

		return menuFacts;
	}
	
}
